package com.zijidelu.luframework.utils.assertion;

import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.lang.func.LambdaUtil;
import com.zijidelu.luframework.utils.CastUtil;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 校验工具类。
 *
 * @author ZIJIDELU
 * @date: 2025/1/2 9:06
 */
@Slf4j
public class FluentValidator<T> {
    private static final Validator validator;
    private final List<Detail<T, ?>> detailList = new ArrayList<>();
    private final List<String> errMsgList = new ArrayList<>();
    private final List<Class<?>> groups = new ArrayList<>();
    private final T data;
    private boolean isValidator = false;

    static {
        try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
            validator = factory.getValidator();
        }
    }

    private FluentValidator(T data, boolean isValidator) {
        this.data = data;
        this.isValidator = isValidator;
    }

    /**
     * 初始化
     *
     * @param data 需要验证的对象
     * @param <T>  验证对象类型
     * @return 当前类对象
     */
    public static <T> FluentValidator<T> init(T data) {
        return new FluentValidator<>(data, false);
    }

    /**
     * 使用Validator进行验证对象的注解
     *
     * @return this
     */
    public FluentValidator<T> useValidator() {
        this.isValidator = true;
        return this;
    }

    /**
     * 添加验证组
     *
     * @param group 验证组
     * @return this
     */
    public FluentValidator<T> addGroup(Class<?> group) {
        groups.add(group);
        return this;
    }

    /**
     * 添加验证字段
     *
     * @param field 字段
     * @param <R>   返回值类型
     * @return this
     */
    public <R> FluentValidator<T> addField(Func1<T, R> field) {
        detailList.add(Detail.<T, R>builder().field(field).build());
        return this;
    }

    /**
     * 添加验证字段
     *
     * @param field     字段
     * @param predicate 验证规则
     * @param <R>       返回值类型
     * @return this
     */
    public <R> FluentValidator<T> addField(Func1<T, R> field, Predicate<R> predicate) {
        detailList.add(Detail.<T, R>builder().field(field).predicate(predicate).build());
        return this;
    }

    /**
     * 添加验证字段
     *
     * @param field     字段
     * @param predicate 验证规则
     * @param msg       验证提示消息
     * @param <R>       返回值类型
     * @return this
     */
    public <R> FluentValidator<T> addField(Func1<T, R> field, Predicate<R> predicate, String msg) {
        detailList.add(Detail.<T, R>builder().field(field).predicate(predicate).msg(msg).build());
        return this;
    }

    /**
     * 验证
     *
     * @param exceptionFunction 自定义异常
     */
    public void valid(Function<String, RuntimeException> exceptionFunction) {
        if (isValidator) {
            // 根据是否设置了验证组，选择不同的验证方式
            Set<ConstraintViolation<T>> validate = groups.isEmpty()
                    // 情况1：没有设置验证组，使用默认验证（验证所有注解）
                    ? validator.validate(data)
                    // 情况2：设置了验证组，只验证指定组的注解
                    : validator.validate(data, groups.toArray(new Class[0]));

            // 检查验证结果是否包含错误
            if (CollectionUtils.isNotEmpty(validate)) {
                // 遍历所有验证违规项，收集错误消息
                validate.forEach(constraintViolation ->
                        // 将每个验证失败的消息添加到错误消息列表中
                        errMsgList.add(constraintViolation.getMessage())
                );
            }
        } else {
            detailList.forEach(detail -> {
                try {
                    Object fieldValue = detail.getField().call(data);

                    if (Objects.isNull(detail.getPredicate())) {
                        if (Objects.nonNull(fieldValue)) {
                            return;
                        }
                    } else {
                        if (detail.getPredicate().test(CastUtil.cast(fieldValue))) {
                            return;
                        }
                    }

                    String errMsg;
                    if (Objects.isNull(detail.getMsg())) {
                        errMsg = "[" + LambdaUtil.getFieldName(detail.getField()) + "]字段值不允许为空";
                    } else {
                        errMsg = detail.getMsg();
                    }

                    errMsgList.add(errMsg);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        }

        if (!errMsgList.isEmpty()) {
            String errMSg = String.join(",", errMsgList);
            log.error(errMSg);

            if (Objects.nonNull(exceptionFunction)) {
                throw exceptionFunction.apply(errMSg);
            }

            throw new RuntimeException(errMSg);
        }
    }

    /**
     * 验证
     */
    public void valid() {
        valid(null);
    }

    @Builder
    @Data
    public static class Detail<T, R> {
        private Func1<T, R> field;
        private Predicate<R> predicate;
        private String msg;
    }

}